package de.fhwf.mixedreality.client2008;

import java.awt.Color;

import de.fhwf.mixedreality.client.AgentView;
import de.fhwf.mixedreality.client.AgentViewEx;
import de.fhwf.mixedreality.client.IClient;
import de.fhwf.mixedreality.client.IMovements;
import de.fhwf.mixedreality.client.IPositionsStorage;
import de.fhwf.mixedreality.client.IView;
import de.fhwf.mixedreality.client.UDPConnection;

public class Client2008 implements IClient {
	private UDPConnection mConnection;
	private MessageParser mParser;
	private CommandInterface mCmd;
	private Movements mMovements;
	private PositionsStorage mPositions;
	private long mLastFlush;
	private IView mView;
	private String mServerAddress;
	private int mServerPort;
	private String mBotName;
	private String mTeamName;
	private int mRCID;
	private int mVTID;
	private Team mTeam;

	private long mByteCount = 0;

	/**
	 * This function initializes the client, connecting it to the server. It
	 * returns false on fail or true on success
	 */
	public Client2008(String serverAddress, Team team, int vtID, int rcID) {
		int serverPort = 0;
		if (team == Team.BLUE) {
			serverPort = 3310;
		} else {
			serverPort = 3311;
		}
		
		mServerAddress = serverAddress;
		mServerPort = serverPort;
		mVTID = vtID;
		mRCID = rcID;
		mBotName = "[Bot " + Integer.toString(rcID) + "]";
		mPositions = new PositionsStorage(this, team);
		mCmd = new CommandInterface();
		mParser = new MessageParser(mPositions);
		mMovements = new Movements(mPositions, mCmd);
		mConnection = new UDPConnection(serverAddress, serverPort);
		mTeam = team;
	}

	/**
	 * Same as Client(String, Team), but additionally auto-updates the given view.
	 */
	public Client2008(String serverAddress, Team team, int vtID, int rcID,
			AgentView view) {
		this(serverAddress, team, vtID, rcID);
		mView = view;
	}

	/**
	 * Same as Client(String, Team, AgentView), but additionally sets the bot name.
	 */
	public Client2008(String serverAddress, Team team, int vtID, int rcID,
			AgentView view, String name) {
		this(serverAddress, team, vtID, rcID, view);
		mBotName = name;
	}

	/**
	 * Same as Client(String, Team, AgentView, String), but additionally sets the team name.
	 */
	public Client2008(String serverAddress, Team team, int vtID, int rcID,
			AgentView view, String name, String teamName) {
		this(serverAddress, team, vtID, rcID, view, name);
		mTeamName = teamName;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#update()
	 */
	public boolean update() {
		// Try to receive for about one second
		String recvData = mConnection.receive();
		// LOG.debug("recData: "+recvData);
		return update(recvData);
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#update(java.lang.String)
	 */
	public boolean update(String serverData) {
		// If we have data from the server
		if (!serverData.equals("")) {
			mByteCount += serverData.length() + 2;
			// Parse the received data
			// System.out.println("[Client] update: Received data: " + serverData);
			mParser.parse(serverData);
			if (mView != null) {
				mView.drawClientWorld(this);
			}
			// Return true on success
			return true;
		} else {
			// Return false if there was no message there anymore. This most likely
			// means the connection is lost.
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#flush()
	 */
	public void flush() {
		// Get the computed command
		String cmd = mCmd.getCmd();
		// if a view is attached, update it
		if (mView != null) {
			if (cmd.length() > 0) {
				if (cmd.equals("FNFN") || cmd.equals("BNBN")) {
					mView.drawText(10, 20, "Command: " + cmd, Color.RED);
				} else {
					mView.drawText(10, 20, "Command: " + cmd);
				}
			}
			mView.update(this);
		}
		// Checks whether the command is empty or not
		if (cmd.length() > 0) {
			mByteCount += cmd.length() + 2;
			// Send the command data to the server using the UDP connection
			mConnection.send(cmd);
		}
		// Let's have a delay here because keeping the server busy helps nobody
		int dur = 0;
		if (mLastFlush != 0) {
			dur = (int) ((System.nanoTime() - mLastFlush) / 1000000);
		}
		int sleep = 30 - dur;
		if (sleep > 0) {
			// System.out.println("[Client] flush: Last iteration took " + dur +
			// "ms, waiting " + sleep + "ms");
			try {
				Thread.sleep(sleep); // 15fps
			} catch (InterruptedException e) {
			}
		} else {
			// System.out.println("[Client] flush: Last iteration took " + dur +
			// "ms, not waiting");
		}
		mLastFlush = System.nanoTime();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#close()
	 */
	public void close() {
		mConnection.close();
		// System.out.println("[Client] close: Bytes transferred during game: " +
		// mByteCount + " (" + (mByteCount / 1024) + "kb)");
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getMovements()
	 */
	public IMovements getMovements() {
		return mMovements;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#Movements()
	 */
	public IMovements Movements() {
		return mMovements;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getPositions()
	 */
	public IPositionsStorage getPositions() {
		return mPositions;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#Positions()
	 */
	public IPositionsStorage Positions() {
		return mPositions;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#time()
	 */
	public double time() {
		return mParser.getTime();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#isGameOn()
	 */
	public boolean isGameOn() {
		return mParser.isGameOn();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#myID()
	 */
	public int myID() {
		return mParser.getMyID();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#iAmLost()
	 */
	public boolean iAmLost() {
		return mParser.isLost();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#isOpponentFound(int)
	 */
	public boolean isOpponentFound(int id) {
		return mParser.getOpponentUpdateState(id);
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#isTeammateFound(int)
	 */
	public boolean isTeammateFound(int id) {
		//if (id != myID()) {
			return mParser.getTeammateUpdateState(id);
		//} else {
		//	return false;
		//}
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#goalsTeam()
	 */
	public int goalsTeam() {
		return mParser.getTeamGoals();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#goalsOpponents()
	 */
	public int goalsOpponents() {
		return mParser.getOpponentGoals();
	}

	public GameMode getGameMode() {
		String gm = mParser.getPlayMode();
		if (gm.equals("play on")) {
			return GameMode.PLAY_ON;
		} else if (gm.equals("frozen operator")) {
			return GameMode.FROZEN;
		} else if (gm.equals("frozen match")) {
			return GameMode.FROZEN;
		} else if (gm.equals("kick off")) {
			return GameMode.KICK_OFF;
		} else if (gm.equals("goal kick yellow")) {
			if (mTeam == Team.YELLOW) {
				return GameMode.GOAL_KICK_OWN;
			} else {
				return GameMode.GOAL_KICK_OPPONENT;
			}
		} else if (gm.equals("goal kick blue")) {
			if (mTeam == Team.BLUE) {
				return GameMode.GOAL_KICK_OWN;
			} else {
				return GameMode.GOAL_KICK_OPPONENT;
			}
		} else if (gm.equals("corner kick yellow")) {
			if (mTeam == Team.YELLOW) {
				return GameMode.CORNER_KICK_OWN;
			} else {
				return GameMode.CORNER_KICK_OPPONENT;
			}
		} else if (gm.equals("corner kick blue")) {
			if (mTeam == Team.BLUE) {
				return GameMode.CORNER_KICK_OWN;
			} else {
				return GameMode.CORNER_KICK_OPPONENT;
			}
		} else if (gm.equals("team adjustment") || gm.equals("team adjustmest")) {
			return GameMode.TEAM_ADJUSTMENT;
		} else {
			// If unknown GameMode occurs, return PLAY_ON to make sure the agent does work
			return GameMode.PLAY_ON;
		}
	}
	
	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getView()
	 */
	public IView getView() {
		return mView;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#setView(de.fhwf.mixedreality.client.AgentView)
	 */
	public void setView(IView newView) {
		mView = newView;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#isSimulated()
	 */
	public boolean isSimulated() {
		return (mView instanceof AgentViewEx);
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#peekCmd()
	 */
	@SuppressWarnings("deprecation")
	public String peekCmd() {
		String cmd = mCmd.getCmd();
		mCmd.setRaw(cmd);
		return cmd;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getLastCmd()
	 */
	public String getLastCmd() {
		return mCmd.getLastCmd();
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getServerAddress()
	 */
	public String getServerAddress() {
		return this.mServerAddress;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getServerPort()
	 */
	public int getServerPort() {
		return this.mServerPort;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#setBotName(java.lang.String)
	 */
	public void setBotName(String name) {
		mBotName = name;
	}

	public void setTeamName(String name) {
		mTeamName = name;
	}
	
	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#getBotName()
	 */
	public String getBotName() {
		return mBotName;
	}
	
	public String getTeamName() {
		return mTeamName;
	}
/*
	private boolean connectToServer() {
		String msg = XMLProtocol.ClientToServerConnect(mBotName, mVTID, mRCID);
		mConnection.send(msg);

		int tries = 3;
		boolean receivedResponse = false;
		while (tries >= 0) {
			
			String repl = mConnection.receive();
			if (!repl.equals("")) {
				if (!receivedResponse) {
					if (XMLProtocol.WorldDataApplication(mParser, mPositions, repl)) {
						System.out.println("[Client] connectToServer: Already connected");
						return true;
					}
					receivedResponse = XMLProtocol.receiveConnectionResponse(repl);
					continue;
				} else {
					if (XMLProtocol.ServerToClientConnect(repl)) {
						msg = XMLProtocol.sendConnectionResponse(true);
						mConnection.send(msg);
						return true;
					} else {
						msg = XMLProtocol.sendConnectionResponse(false);
						mConnection.send(msg);
						return false;
					}
				}
			}

			tries--;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.err.println("[Client] connectToServer: Connection failed");
		return false;
	}
*/
	private boolean connectToServer1_1() {
		/*
		String repl = mConnection.receive();
		if (!repl.equals("")) {
			if (XMLProtocol.WorldDataApplication(mParser, mPositions, repl)) {
				System.out.println("[Client] connectToServer: Already connected");
				return true;
			}
		}
		*/
		
		String msg = XMLProtocol.ClientToServerConnect1_1();
		mConnection.send(msg);

		int tries = 3;
		boolean receivedResponse = false;
		boolean waitingForParameters = false;
		while (tries >= 0) {
			
			String repl = mConnection.receive();
			if (!repl.equals("")) {
				if (!receivedResponse) {
					if (XMLProtocol.WorldDataApplication(mParser, mPositions, repl)) {
						System.out.println("[Client] connectToServer: Already connected");
						return true;
					}
					receivedResponse = XMLProtocol.receiveConnectionResponse(repl);
					continue;
				} else {
					if (waitingForParameters) {
						if (XMLProtocol.receiveParameters(repl)) {
							msg = XMLProtocol.sendParameters(mRCID, mVTID, mBotName, mTeamName);
							mConnection.send(msg);							
							return true;
						} else {
							return false;
						}
					} else {
						if (XMLProtocol.ServerToClientConnect(repl)) {
							msg = XMLProtocol.sendConnectionResponse(true);
							mConnection.send(msg);
							waitingForParameters = true;
							continue;
						} else {
							msg = XMLProtocol.sendConnectionResponse(false);
							mConnection.send(msg);
							return false;
						}
					}
				}
			}

			tries--;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.err.println("[Client] connectToServer: Connection failed");
		return false;
	}

	/* (non-Javadoc)
	 * @see de.fhwf.mixedreality.client2008.IClient#connect()
	 */
	public boolean connect() {
		System.out.println("[Client] Connecting to server...");
		//return connectToServer();
		return connectToServer1_1();
	}
}